﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Sgml.XPath;

namespace Engine.Sgml {
    public sealed partial class SAttribute {
        /// <summary>
        /// Determines if this attribute is a namespace declaration.
        /// </summary>
        /// <returns>true if this attribute is a namespace declaration; otherwise false.</returns>
        internal bool IsNamespace() {
            return object.ReferenceEquals(Prefix, SName.Xmlns) ||
                   object.ReferenceEquals(LocalName, SName.Xmlns);
            }

        /// <summary>
        /// Gets a value indicating whether this attribute has a next 
        /// </summary>
        /// <returns>true if this attribute has a next attribute; otherwise false.</returns>
        internal bool HasNextAttribute() {
            if (Parent == null)
                return false;
            return Parent.Attributes.Count > OrderIndex + 1;
            }

        /// <summary>
        /// Gets the next attribute of the parent element.
        /// </summary>
        /// <returns>A SAttribute containing the next attribute of the parent element.</returns>
        public SAttribute NextAttribute {
            get {
                if (object.ReferenceEquals(Parent, null))
                    return null;
                else
                    return Parent.Attributes[OrderIndex + 1];
                }
            }

        /// <summary>
        /// Gets the previous attribute of the parent element.
        /// </summary>
        /// <returns>A SAttribute containing the previous attribute of the parent element.</returns>
        public SAttribute PreviousAttribute {
            get {
                if (object.ReferenceEquals(Parent, null))
                    return null;
                else
                    return Parent.Attributes[OrderIndex - 1];
                }
            }

        /// <summary>
        /// Removes this attribute from its parent element.
        /// </summary>
        public void Remove() {
            if (Parent == null)
                return;
            OnChanging(this, SObjectChangeEventArgs.Remove);
            Parent.Attributes.RemoveAt(OrderIndex);
            Parent = null;
            OnChanged(this, SObjectChangeEventArgs.Remove);
            }

        /// <summary>
        /// Adds the specified attribute immediately before this 
        /// </summary>
        /// <param name="attribute">An attribute to be added before this </param>
        /// <exception cref="System.ArgumentNullException">If an <paramref name="attribute"/> is null.</exception>
        /// <exception cref="System.InvalidOperationException">If the attribute before which adds the specified <paramref name="attribute"/> is not in a document tree.</exception>
        public void AddBeforeSelf(SAttribute attribute) {
            Guard.ArgumentNotNull(attribute, "attribute");
            if (Parent == null)
                throw new InvalidOperationException("The attribute before which adds the specified attribute is not in a document tree.");
            if (attribute.Parent != null)
                attribute.Remove();
            OnChanging(attribute, SObjectChangeEventArgs.Add);
            Parent.Attributes.Insert(attribute, OrderIndex);
            attribute.Parent = Parent;
            OnChanged(attribute, SObjectChangeEventArgs.Add);
            }

        /// <summary>
        /// Adds the specified attribute immediately after this 
        /// </summary>
        /// <param name="attribute">An attribute to be added after this </param>
        /// <exception cref="System.ArgumentNullException">If an <paramref name="attribute"/> is null.</exception>
        /// <exception cref="System.InvalidOperationException">If the attribute before which adds the specified <paramref name="attribute"/> is not in a document tree.</exception>
        public void AddAfterSelf(SAttribute attribute) {
            Guard.ArgumentNotNull(attribute, "attribute");
            if (Parent == null)
                throw new InvalidOperationException("The attribute after which adds the specified attribute is not in a document tree.");
            if (attribute.Parent != null)
                attribute.Remove();
            OnChanging(attribute, SObjectChangeEventArgs.Add);
            Parent.Attributes.Insert(attribute, OrderIndex + 1);
            attribute.Parent = Parent;
            OnChanged(attribute, SObjectChangeEventArgs.Add);
            }

        /// <summary>
        /// Gets the string that represents the XPath for the specified 
        /// </summary>
        /// <returns>A string that contain XPath expression.</returns>
        internal string XPath {
            get { return XPathBuilder.BuildAbsolute(this); }
            }
        }
    }
